package gopcep

import (
	"context"
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/keepalive"

	"pceservice/pkg/gopcep/api"
)

const (
	// 默认连接超时时间
	defaultConnectTimeout = 10 * time.Second
	// 默认重连间隔
	defaultReconnectInterval = 5 * time.Second
	// 发送消息缓冲区大小
	sendBufferSize = 100
)

// Config GoPCEP客户端配置
type Config struct {
	Address string `yaml:"address"`
}

// PCEPClient 表示PCEP客户端
type PCEPClient struct {
	conn        *grpc.ClientConn
	client      api.GopcepApiClient
	config      Config
	isConnected bool
	mu          sync.RWMutex
	sendCh      chan *api.InitiatedLsp
	ctx         context.Context
	cancel      context.CancelFunc
	reconnectCh chan struct{}
}

// NewPCEPClient 创建一个新的PCEP客户端
func NewPCEPClient(config Config) (*PCEPClient, error) {
	ctx, cancel := context.WithCancel(context.Background())
	return &PCEPClient{
		config:      config,
		sendCh:      make(chan *api.InitiatedLsp, sendBufferSize),
		ctx:         ctx,
		cancel:      cancel,
		reconnectCh: make(chan struct{}, 1),
	}, nil
}

// Connect 连接到PCEP服务器
func (c *PCEPClient) Connect() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.isConnected {
		return nil
	}

	ctx, cancel := context.WithTimeout(context.Background(), defaultConnectTimeout)
	defer cancel()

	var opts []grpc.DialOption
	opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
	opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
		Time:                10 * time.Second, // 每10秒ping一次
		Timeout:             3 * time.Second,  // 如果3秒内没有响应，认为连接断开
		PermitWithoutStream: true,             // 允许在没有活动流的情况下发送ping
	}))

	conn, err := grpc.DialContext(ctx, c.config.Address, opts...)
	if err != nil {
		return fmt.Errorf("连接PCEP服务器失败: %v", err)
	}

	c.conn = conn
	c.client = api.NewGopcepApiClient(conn)
	c.isConnected = true

	// 启动消息发送协程
	go c.startMessageSender()
	// 启动连接监控协程
	go c.monitorConnection()

	log.Printf("成功连接到PCEP服务器: %s", c.config.Address)
	return nil
}

// Close 关闭连接
func (c *PCEPClient) Close() {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.cancel != nil {
		c.cancel()
	}

	if c.conn != nil {
		c.conn.Close()
		c.conn = nil
	}

	c.isConnected = false
	close(c.sendCh)
	close(c.reconnectCh)

	log.Printf("已关闭与PCEP服务器的连接: %s", c.config.Address)
}

// SendMessage 发送PCEP消息
func (c *PCEPClient) SendMessage(msg *api.InitiatedLsp) error {
	c.mu.RLock()
	defer c.mu.RUnlock()

	if !c.isConnected {
		return errors.New("未连接到PCEP服务器")
	}

	select {
	case c.sendCh <- msg:
		return nil
	case <-c.ctx.Done():
		return errors.New("客户端已关闭")
	}
}

// 启动消息发送协程
func (c *PCEPClient) startMessageSender() {
	for {
		select {
		case msg, ok := <-c.sendCh:
			if !ok {
				return
			}

			c.mu.RLock()
			if !c.isConnected || c.client == nil {
				c.mu.RUnlock()
				log.Println("无法发送消息: 未连接到服务器")
				continue
			}

			_, err := c.client.InitiateLsp(c.ctx, msg)
			c.mu.RUnlock()

			if err != nil {
				log.Printf("发送PCEP消息失败: %v", err)
				// 触发重连
				select {
				case c.reconnectCh <- struct{}{}:
				default:
				}
			}
		case <-c.ctx.Done():
			return
		}
	}
}

// 监控连接状态
func (c *PCEPClient) monitorConnection() {
	ticker := time.NewTicker(5 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			c.checkConnection()
		case <-c.reconnectCh:
			c.attemptReconnect()
		case <-c.ctx.Done():
			return
		}
	}
}

// 检查连接状态
func (c *PCEPClient) checkConnection() {
	c.mu.RLock()
	defer c.mu.RUnlock()

	if c.conn == nil {
		return
	}

	state := c.conn.GetState()
	if state == connectivity.TransientFailure || state == connectivity.Shutdown {
		log.Printf("连接状态异常: %s，尝试重连", state.String())
		// 触发重连
		select {
		case c.reconnectCh <- struct{}{}:
		default:
		}
	}
}

// 尝试重连
func (c *PCEPClient) attemptReconnect() {
	c.mu.Lock()
	defer c.mu.Unlock()

	// 如果已经在连接中或已经连接，不进行重连
	if !c.isConnected && c.conn != nil {
		c.conn.Close()
		c.conn = nil
	}

	if c.isConnected {
		return
	}

	log.Printf("开始尝试重连到PCEP服务器: %s", c.config.Address)

	for {
		select {
		case <-c.ctx.Done():
			log.Println("重连被取消")
			return
		default:
			ctx, cancel := context.WithTimeout(context.Background(), defaultConnectTimeout)
			conn, err := grpc.DialContext(ctx, c.config.Address,
				grpc.WithTransportCredentials(insecure.NewCredentials()),
				grpc.WithBlock(),
			)
			cancel()

			if err == nil {
				c.conn = conn
				c.client = api.NewGopcepApiClient(conn)
				c.isConnected = true
				log.Printf("重连成功: %s", c.config.Address)
				return
			}

			// log.Printf("重连失败: %v，%d秒后重试", err, defaultReconnectInterval.Seconds())
			select {
			case <-time.After(defaultReconnectInterval):
			case <-c.ctx.Done():
				log.Println("重连被取消")
				return
			}
		}
	}
}
